/**********************************************************************************************************************
 *  -------------------------------------------------------------------------------------------------------------------
 *  file description
 *  -------------------------------------------------------------------------------------------------------------------
 *         \file  vkern.c
 *         \unit  vkern
 *        \brief  This is a simple multi-timed task scheduling kernel
 *   \transplant  1. Initialize the kernel, pass in the function to obtain the time slice and the size of the time slice
 *                2. Create tasks, configure task periods, callback functions, etc.
 *                3. Call the vtask_schedule function in an infinite loop
 *      \details  v1.0.0
 *********************************************************************************************************************/
#include "vkern.h"
#include "vk_type.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>

#define ABS(x)					((x)>0?(x):-(x))

/* kernel manager */
static vkern_manage_t manage;

#ifdef KERN_USING_ERROR 
ke_ret_t _error(ke_ret_t e, char* file, int line)
{
	if (e != KE_OK)
	{
		printf("kern error<%d>: file<%s>, line<%d>\r\n", e, file, line);
	}

	return e;
}
#endif

/** Show vkern logo.
 *  \param[in] none.
 *  \return none
 */
static void vkern_logo(void)
{
	printf("+-----------------------------------------------------+\r\n");
	printf("|+---------------------------------------------------+|\r\n");
	printf("||             _     _                        V1.0.0 ||\r\n");
	printf("||            | |   / /                              ||\r\n");
	printf("||            | |  / /                               ||\r\n");
	printf("||            | | / /                                ||\r\n");
	printf("||    _    _  | |/ /     _____    _   __  ______     ||\r\n");
	printf("||   | |  | | |    \\    / ___ \\  | |_/ / |  __  |    ||\r\n");
	printf("||   | |  | | |  /\\ \\  / ______\\ |  . /  | |  | |    ||\r\n");
	printf("||   | |  | | | |  \\ \\ \\ \\   __  |  _/   | |  | |    ||\r\n");
	printf("||    \\ \\/ /  | |   \\ \\ \\ \\_/ /  | |     | |  | |    ||\r\n");
	printf("||     \\__/   |_|    \\_\\ \\___/   |_|     |_|  \\_\\    ||\r\n");
	printf("||                                                   ||\r\n");
	printf("|+---------------------------------------------------+|\r\n");
	printf("+-----------------------------------------------------+\r\n");
}

/** Create task list.
 *  \param[in] none.
 *  \return vtask list handler
 *          NULL: fail
 */
static vtask_list_t vtask_list_create(void)
{
	vtask_list_t list = NULL;

	list = (vtask_list_t)malloc(sizeof(VTASK_LIST));
	if (list == NULL)
	{
		return NULL;
	}

	list->head = (vtask_list_node_t)malloc(sizeof(VTASK_LIST_NODE));
	if (list->head == NULL)
	{
		free(list);
		return NULL;
	}

	list->tail = (vtask_list_node_t)malloc(sizeof(VTASK_LIST_NODE));
	if (list->tail == NULL)
	{
		free(list->head);
		free(list);
		return NULL;
	}

	list->head->next = list->tail;
	list->head->prev = NULL;
	list->tail->prev = list->head;
	list->tail->next = NULL;

	list->len = 0;

	return list;
}

/** Delete task list.
 *  \param[in] list: vtask list handler.
 *  \return none
 */
static void vtask_list_delete(vtask_list_t list)
{
	free(list->head);
	free(list->tail);
	free(list);
}

/** Add the task control block to the list.
 *  \param[in] list: vtask list handler.
 *  \param[in] tcb: vtask control block.
 *  \return ke_ret_t
 */
static ke_ret_t vtask_list_add(vtask_list_t list, vtcb_t tcb)
{
	ke_ret_t ret = KE_OK;
	vtask_list_node_t cur_node = NULL;
	vtask_list_node_t next_node = NULL;
	vtask_list_node_t prev_node = NULL;
	vtask_list_node_t temp_node = NULL;

	temp_node = (vtask_list_node_t)malloc(sizeof(VTASK_LIST_NODE));
	if (temp_node != NULL)
	{
		temp_node->tcb = tcb;

		if (list == manage.ready_tasks) /* ready tasks are sorted by priority */
		{
			/* traverse the task linked list to find the insertion position, starting from the end */
			cur_node = list->tail;
			while (cur_node->prev != list->head)
			{
				/* the previous task priority is higher than or equal to the inserted task priority */
				if (cur_node->prev->tcb->prio <= temp_node->tcb->prio) 
				{
					/* The current task is lower than the inserted task or the current task is just at the end */
					if ((cur_node == list->tail) || (cur_node->tcb->prio > temp_node->tcb->prio)) 
					{
						break;
					}
				}

				cur_node = cur_node->prev;
			}
		}
		else if (list == manage.all_tasks) /* all task list is sorted by task id */ 
		{
			/* traverse the task linked list to find the insertion position, starting from the end */
			cur_node = list->tail;
			while (cur_node->prev != list->head)
			{
				/* The previous task is less than or equal to the inserted task */
				if (cur_node->prev->tcb->task <= temp_node->tcb->task)
				{
					/* The current task is larger than the inserted task or the current task is at the end */
					if ((cur_node == list->tail) || (cur_node->tcb->task > temp_node->tcb->task))
					{
						break;
					}
				}

				cur_node = cur_node->prev;
			}
		}
		else
		{
			return KE_TASK_LIST_NULL;
		}
		
		/* link */
		next_node = cur_node;
		prev_node = next_node->prev;

		prev_node->next = temp_node;
		temp_node->prev = prev_node;

		next_node->prev = temp_node;
		temp_node->next = next_node;

		list->len++;
	}
	else
	{
		ret = KE_ALLOC_FAIL;
	}
	
	return ret;
}

/** Remove the task control block from the task list.
 *  \param[in] list: vtask list handler.
 *  \param[in] tcb: vtask control block.
 *  \return ke_ret_t
 */
static ke_ret_t vtask_list_remove(vtask_list_t list, vtcb_t tcb)
{
	ke_ret_t ret = KE_OK;
	vtask_list_node_t cur_node = NULL;
	vtask_list_node_t next_node = NULL;
	vtask_list_node_t prev_node = NULL;
	int index = 0;

	if (list->head->next == list->tail)
	{
		ret = KE_TASK_LIST_NULL;
	}
	else
	{
		cur_node = list->head->next;
		index = 0;

		/* search from beginning to end */
		while (cur_node != list->tail)
		{
			index++;
			if (cur_node->tcb == tcb)
			{
				break;
			}

			cur_node = cur_node->next;
		}
	}

	if (index != 0)
	{
		next_node = cur_node->next;
		prev_node = cur_node->prev;

		prev_node->next = next_node;
		next_node->prev = prev_node;

		list->len--;
		free(cur_node);

		ret = KE_OK;
	}
	else
	{
		ret = KE_NO_THIS_TASK;
	}
	
	return ret;
}

/** Find task in the list.
 *  \param[in] list: vtask list handler.
 *  \param[in] task: vtask handler.
 *  \return task node in the list
 *          NULL: fail
 */
static vtask_list_node_t vtask_list_find(vtask_list_t list, vtask_t task)
{
	vtask_list_node_t t_node = NULL;
	
	if (list->head->next == list->tail)
	{
		t_node = NULL;
	}
	else /* Traverse the task linked list to find the insertion position, starting from the end */
	{
		t_node = list->head->next;

		while (t_node != list->tail)
		{
			if (t_node->tcb->task == task)
			{
				break;
			}

			t_node = t_node->next;
		}

		if (t_node == list->tail)
		{
			t_node = NULL;
		}
	}
	
	return t_node;
}

/** Allocate idle task.
 *  \param[in] none.
 *  \return idle task
 *          0: fail
 */
static vtask_t vtask_alloc(void)
{
	vtask_t task = 1; /* Assign from 1 */
	vtcb_t tcb = NULL;
	vtask_list_node_t node = NULL;

	node = manage.all_tasks->head->next;
	while (node != manage.all_tasks->tail)
	{
		tcb = node->tcb;
		if (task < tcb->task)
		{
			return task;
		}
		else
		{
			task = tcb->task + 1;
		}

		node = node->next;
	}

	if (node == manage.all_tasks->tail)
	{
		return task;
	}

	return 0;
}

/** kernel initialization.
 *  \param[in] tick_func: the kernel gets the time callback function.
 *  \param[in] time_slice: time slice length, unit ms.
 *  \return ke_ret_t
 */
ke_ret_t vkern_init(vkern_tick_t tick_func, uint16_t time_slice)
{
	ke_ret_t ret = KE_OK;

	manage.tick = tick_func;
	manage.tick_pre = manage.tick();
	manage.tick_cur = manage.tick_pre;
	manage.task_num = 0;
	manage.time_slice = time_slice;

	manage.all_tasks = vtask_list_create();
	if (manage.all_tasks == NULL)
	{
		ret = KE_ALLOC_FAIL;
	}

	manage.ready_tasks = vtask_list_create();
	if (manage.ready_tasks == NULL)
	{
		vtask_list_delete(manage.all_tasks);
		ret = KE_ALLOC_FAIL;
	}

	if (ret == KE_OK)
	{
		vkern_logo();
	}

	return ret;
}

/** create task.
 *  \param[in] task: the specified task handle.
 *  \param[in] prio: task priority, the smaller the higher the priority.
 *  \param[in] state: task state, @ref vtask_state_t,
					VTASK_STATE_WAIT -- wait for a period after the task is created before running
					VTASK_STATE_READY -- run immediately after the task is created
 *  \param[in] period: task execution cycle, run the task function every period milliseconds.
 *  \param[in] handler: task callback function.
 *  \return ke_ret_t
 */
ke_ret_t vtask_create(vtask_t task, uint8_t prio, vtask_state_t state, uint16_t period, task_handler_t handler)
{
	ke_ret_t ret = KE_OK;
	vtcb_t tcb = NULL;
	uint32_t task_index = 0;
	vtask_list_node_t t_node = NULL;

#if (TASKS_NUMBER_MAX != 0)
	/* determine whether the number of tasks exceeds the limit */
	if (manage.task_num >= TASKS_NUMBER_MAX)
	{
		return KE_TASK_NUM_OVER;
	}
#endif

	/* task cannot be 0 */
	if (task == 0)
	{
		return KE_TASK_ID_ERR;
	}
	
	/* find tasks to be created and determine whether they are repeated */
	t_node = vtask_list_find(manage.all_tasks, task);
	if(t_node != NULL)
	{
		return KE_TASK_REPEAT;
	}
	
	/* Allocate task control block space */
	tcb = (vtcb_t)malloc(sizeof(VTCB));
	if (tcb != NULL)
	{
		tcb->task = task;
		tcb->prio = prio;
		tcb->state = state;
		tcb->period = period;
		tcb->time = period / manage.time_slice;
		tcb->handler = handler;

		ret = vtask_list_add(manage.all_tasks, tcb);
		if (ret == KE_OK)
		{
			manage.task_num++;

			if (tcb->state == VTASK_STATE_READY)
			{
				if (vtask_list_add(manage.ready_tasks, tcb) == KE_OK)
				{
					tcb->time = 0;
				}
			}
		}
		else
		{
			free(tcb);
		}
	}
	else
	{
		return KE_ALLOC_FAIL;
	}

	return ret;
}

/** create task, automatically assign task.
 *  \param[in] prio: task priority, the smaller the higher the priority.
 *  \param[in] state: task state, @ref vtask_state_t,
					VTASK_STATE_WAIT -- wait for a period after the task is created before running
					VTASK_STATE_READY -- run immediately after the task is created
 *  \param[in] period: task execution cycle, run the task function every period milliseconds.
 *  \param[in] handler: task callback function.
 *  \return task handle
 */
vtask_t vtask_create_dynamic(uint8_t prio, vtask_state_t state, uint16_t period, task_handler_t handler)
{
	vtask_t task = 0;

	task = vtask_alloc();
	if (task == 0)
	{
		return 0;
	}

	if (vtask_create(task, prio, state, period, handler) != KE_OK)
	{
		return 0;
	}

	return task;
}

/** delete task.
 *  \param[in] task: task handle.
 *  \return ke_ret_t
 */
ke_ret_t vtask_delete(vtask_t task)
{
	ke_ret_t ret = KE_OK;
	vtask_list_node_t t_node = NULL;

	t_node = vtask_list_find(manage.all_tasks, task);
	if(t_node == NULL)
	{
		return KE_NO_THIS_TASK;
	}
	
	ret = vtask_list_remove(manage.all_tasks, t_node->tcb);
	if (ret != KE_OK)
	{
		return KE_NO_THIS_TASK;
	}

	free(t_node->tcb);
	manage.task_num--;
	
	return ret;
}

/** attach task to list.
 *  \param[in] tcb: task control block.
 *  \return ke_ret_t
 */
ke_ret_t vtask_attach(vtcb_t tcb)
{
	ke_ret_t ret = KE_OK;

#if (TASKS_NUMBER_MAX != 0)
	/* determine whether the number of tasks exceeds the limit */
	if (manage.task_num >= TASKS_NUMBER_MAX)
	{
		return KE_TASK_NUM_OVER;
	}
#endif

	if (tcb->time > tcb->period / manage.time_slice)
	{
		tcb->time = tcb->period / manage.time_slice;
	}

	ret = vtask_list_add(manage.all_tasks, tcb);
	if (ret == KE_OK)
	{
		manage.task_num++;

		if (tcb->state == VTASK_STATE_READY)
		{
			if (vtask_list_add(manage.ready_tasks, tcb) == KE_OK)
			{
				tcb->time = 0;
			}
		}
	}

	return ret;
}

/** dettach task from list.
 *  \param[in] tcb: task control block.
 *  \return ke_ret_t
 */
ke_ret_t vtask_dettach(vtcb_t tcb)
{
	ke_ret_t ret = KE_OK;
	vtask_list_node_t t_node = NULL;
	
	if (tcb == NULL)
	{
		return KE_NO_THIS_TASK;
	}
	t_node = vtask_list_find(manage.all_tasks, tcb->task);

	if (t_node == NULL)
	{
		return KE_NO_THIS_TASK;
	}

	ret = vtask_list_remove(manage.all_tasks, t_node->tcb);

	if (ret == KE_OK)
	{
		manage.task_num--;
	}

	return ret;
}

/** set task state.
 *  \param[in] task: task handler.
 *  \param[in] state: task state, @ref VTASK_STATE.
 *  \return ke_ret_t
 */
static ke_ret_t vtask_set_state(vtask_t task, vtask_state_t state)
{
	ke_ret_t ret = KE_OK;
	vtask_list_node_t t_node = NULL;
	vtcb_t tcb = NULL;
	
	t_node = vtask_list_find(manage.all_tasks, task);
	if (t_node == NULL)
	{
		return KE_NO_THIS_TASK;
	}
	
	tcb = t_node->tcb;
	tcb->state = state;

	t_node = vtask_list_find(manage.ready_tasks, tcb->task);
	if (t_node != NULL)
	{
		tcb = t_node->tcb;
		vtask_list_remove(manage.ready_tasks, tcb);
	}
	
	return ret;
}

/** suspend task.
 *  \param[in] task: task handler.
 *  \return ke_ret_t
 */
ke_ret_t vtask_suspend(vtask_t task)
{
	return vtask_set_state(task, VTASK_STATE_SUSPEND);
}

/** resume task.
 *  \param[in] task: task handler.
 *  \return ke_ret_t
 */
ke_ret_t vtask_resume(vtask_t task)
{
	return vtask_set_state(task, VTASK_STATE_WAIT);
}

/** get the total number of tasks currently participating in management.
 *  \param[in] none.
 *  \return number of tasks
 */
uint32_t vtask_num(void)
{
	return manage.task_num;
}

/** get the currently running task.
 *  \param[in] none.
 *  \return task handler
 */
vtask_t vtask_runnning(void)
{
	if (manage.running.state == 1)
	{
		return manage.running.task;
	}
	else
	{
		return 0;
	}
}

/** task schedule.
 *  \param[in] none.
 *  \return none
 */
void vtask_schedule(void)
{
	vtask_list_node_t task_list_node = NULL;
	vtcb_t tcb = NULL;

	/* update the current kernel time */
	manage.tick_cur = manage.tick();

	/* Kernel time is normal, no overflow occurs */
	if (ABS(manage.tick_cur - manage.tick_pre) > 0)
	{
		manage.tick_pre = manage.tick_cur;

		/* Traverse each task in the task list */
		task_list_node = manage.all_tasks->head;
		while (task_list_node->next != manage.all_tasks->tail)
		{
			task_list_node = task_list_node->next;
			tcb = task_list_node->tcb;

			if (tcb->handler == FUNCTION_NULL) /* The current task has no specific execution function */
			{
				continue;
			}
			else /* has a specific execution function */
			{
				/* task is not pending */
				if (tcb->state != VTASK_STATE_SUSPEND)
				{
					/* The task time is not up yet, let the task count down */
					if ((tcb->time > 0) && (tcb->state != VTASK_STATE_SUSPEND))
					{
						tcb->time--;
					}
					/* When the task is due, change the task state */
					else
					{
						if (tcb->state == VTASK_STATE_WAIT)
						{
							/* Add the task to the preparation queue according to the task priority */
							if (vtask_list_add(manage.ready_tasks, tcb) == KE_OK)
							{
								tcb->state = VTASK_STATE_READY;
							}
						}
					}
				}
			}
		}
	}

	/* Extract task task out to run */
	task_list_node = manage.ready_tasks->head->next;
	if (task_list_node != manage.ready_tasks->tail)
	{
		tcb = task_list_node->tcb;
		if (tcb->state == VTASK_STATE_READY)
		{
			if (tcb->handler != NULL)
			{
				manage.running.state = 1;
				manage.running.task = tcb->task;
				vtask_list_remove(manage.ready_tasks, tcb);
				tcb->state = VTASK_STATE_RUNNING;
				tcb->handler(NULL);
				tcb->state = VTASK_STATE_WAIT;
				tcb->time = tcb->period / manage.time_slice;
				manage.running.state = 0;
			}
		}
		else
		{
			vtask_list_remove(manage.ready_tasks, tcb);
		}
	}
}

/** traverse tasks.
 *  \param[in] list: list type, 0 -- all tasks, 1 -- ready tasks.
 *  \return none
 */
void vtask_list(uint8_t list)
{
	vtask_list_node_t temp_node = NULL;
	vtask_list_node_t head_node = NULL;
	vtask_list_node_t tail_node = NULL;
	vtcb_t t_task;

	if (list == 0)
	{
		printf("all task: \r\n");
		head_node = manage.all_tasks->head;
		tail_node = manage.all_tasks->tail;
	}
	else if (list == 1)
	{
		printf("ready task: \r\n");
		head_node = manage.ready_tasks->head;
		tail_node = manage.ready_tasks->tail;
	}
	else
	{
		return;
	}
	
	temp_node = head_node;
	while (temp_node->next != tail_node)
	{
		temp_node = temp_node->next;

		t_task = temp_node->tcb;
		printf("task<%d>: prio=%d, state=%d, time=%d/%d\r\n", t_task->task, t_task->prio, t_task->state, t_task->time, t_task->period);
	}
}
